/**
 * 
 */
package galacticthrone.screen.text;

import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.media.opengl.GL;

import org.apache.log4j.Logger;

/**
 * The base class for all my vector fonts.
 *
 * @author Jaco van der Westhuizen
 */
@Deprecated
public class VectorFont
{
	static final Logger logger = Logger.getLogger(VectorFont.class);

	protected static interface Segment
	{
		void drawOutline(GL gl);
		void drawFill(GL gl, Color color);
	}
	
	protected class SegmentStraight implements Segment
	{
		final int startX;
		final int startY;
		final int endX;
		final int endY;
		final float thickX;	// As a fraction of spacing
		final float thickY;	// As a fraction of spacing
		final float dirX;
		final float dirY;

		/**
         * @param startX
         * @param startY
         * @param endX
         * @param endY
         * @param thickX
         * @param thickY
         */
        SegmentStraight(int startX, int startY, int endX, int endY, float thickX, float thickY)
        {
	        this.startX = startX;
	        this.startY = startY;
	        this.endX = endX;
	        this.endY = endY;
	        this.thickX = thickX;
	        this.thickY = thickY;
	        this.dirX = ((endX >= startX) ? 1 : -1) / 2f;
	        this.dirY = ((endY >= startY) ? 1 : -1) / 2f;
        }

        /* (non-Javadoc)
         * @see galacticthrone.screen.text.VectorFont.Segment#drawOutline(javax.media.opengl.GL)
         */
        @Override
        public void drawOutline(GL gl)
        {
        	final float thickXO = dirX * (((thickX+1) > 1 - 1 / spaceX) ? spaceX - 1 : (thickX+1) * spaceX);
        	final float thickYO = dirY * (((thickY+1) > 1 - 1 / spaceY) ? spaceY - 1 : (thickY+1) * spaceY);

        	gl.glBegin(GL.GL_QUADS);
        	gl.glColor4f(0, 0, 0, 1);
	        gl.glVertex2f(offset + spaceX * startX - thickXO, spaceY * startY - thickYO);
	        gl.glVertex2f(offset + spaceX * startX - thickXO, spaceY *   endY + thickYO);
	        gl.glVertex2f(offset + spaceX *   endX + thickXO, spaceY *   endY + thickYO);
	        gl.glVertex2f(offset + spaceX *   endX + thickXO, spaceY * startY - thickYO);
	        gl.glEnd();
        }

		/* (non-Javadoc)
         * @see galacticthrone.screen.text.Font.Segment#draw(javax.media.opengl.GL, float, float, float)
         */
        @Override
        public void drawFill(GL gl, Color color)
        {
        	final float thickXO = dirX * ((thickX > 1 - 1 / spaceX) ? spaceX - 1 : thickX * spaceX);
        	final float thickYO = dirY * ((thickY > 1 - 1 / spaceY) ? spaceY - 1 : thickY * spaceY);

        	gl.glBegin(GL.GL_QUADS);
        	gl.glColor4f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f, 1.0f);
        	gl.glVertex2f(offset + spaceX * startX - thickXO, spaceY * startY - thickYO);
	        gl.glVertex2f(offset + spaceX *   endX + thickXO, spaceY * startY - thickYO);
	        gl.glVertex2f(offset + spaceX *   endX + thickXO, spaceY *   endY + thickYO);
	        gl.glVertex2f(offset + spaceX * startX - thickXO, spaceY *   endY + thickYO);
	        gl.glEnd();
	        
        }
	}
	
	protected class SegmentCurvedCW implements Segment
	{
		final float centerX;
		final float centerY;
		final float multX;
		final float multY;
		final float thickX;	// As a fraction of spacing
		final float thickY;	// As a fraction of spacing

		/**
         * @param startX
         * @param startY
         * @param endX
         * @param endY
         * @param thickX
         * @param thickY
         */
        SegmentCurvedCW(int startX, int startY, int endX, int endY, float thickX, float thickY)
        {
        	boolean dir = !((endX > startX) ^ (endY > startY));

        	this.centerX = (dir) ? endX : startX;
        	this.centerY = (dir) ? startY : endY;
        	this.multX = (dir) ? startX - endX : endX - startX;
        	this.multY = (dir) ? endY - startY : startY - endY;
	        this.thickX = thickX;
	        this.thickY = thickY;//(multY > 0) ? thickY : -thickY;
        }
        
        @Override
        public void drawOutline(GL gl)
        {
        	final float thickXO = ((multX > 0) ? 0.5f : -0.5f) * (((thickX+1) > 1 - 1 / spaceX) ? spaceX - 1 : (thickX+1) * spaceX);
        	final float thickYO = ((multY > 0) ? 0.5f : -0.5f) * (((thickY+1) > 1 - 1 / spaceY) ? spaceY - 1 : (thickY+1) * spaceY);

        	gl.glBegin(GL.GL_TRIANGLE_STRIP);
        	gl.glColor4f(0, 0, 0, 1);

        	for (int i = 0; i <= numSlices; i++)
        	{
        		gl.glVertex2f((spaceX * multX + thickXO) * cosi[i] + offset + spaceX * centerX, (spaceY * multY + thickYO) * sini[i] + spaceY * centerY);	// Outside
        		gl.glVertex2f((spaceX * multX - thickXO) * cosi[i] + offset + spaceX * centerX, (spaceY * multY - thickYO) * sini[i] + spaceY * centerY);	// Inside
        	}
        	
        	gl.glEnd();
        }

		/* (non-Javadoc)
         * @see galacticthrone.screen.text.Font.Segment#draw(javax.media.opengl.GL)
         */
        @Override
        public void drawFill(GL gl, Color color)
        {
        	final float thickXO = ((multX > 0) ? 0.5f : -0.5f) * ((thickX > 1 - 1 / spaceX) ? spaceX - 1 : thickX * spaceX);
        	final float thickYO = ((multY > 0) ? 0.5f : -0.5f) * ((thickY > 1 - 1 / spaceY) ? spaceY - 1 : thickY * spaceY);

        	gl.glBegin(GL.GL_TRIANGLE_STRIP);
        	gl.glColor4f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f, 1.0f);

        	for (int i = 0; i <= numSlices; i++)
        	{
        		gl.glVertex2f((spaceX * multX + thickXO) * cosi[i] + offset + spaceX * centerX, (spaceY * multY + thickYO) * sini[i] + spaceY * centerY);	// Outside
        		gl.glVertex2f((spaceX * multX - thickXO) * cosi[i] + offset + spaceX * centerX, (spaceY * multY - thickYO) * sini[i] + spaceY * centerY);	// Inside
//        		gl.glVertex2f((multX + thickXO) * cosi[i] + centerX, (multY + thickYO) * sini[i] + centerY);	// Outside
//        		gl.glVertex2f((multX - thickXO) * cosi[i] + centerX, (multY - thickYO) * sini[i] + centerY);	// Inside
        	}
        	
        	gl.glEnd();
        }
	}

	protected static class Symbol
	{
		final int width;
		final Segment[] segs;
		
		Symbol(int width, List<Segment> segs)
		{
			this.width = width;
			this.segs  = (Segment[]) segs.toArray();
		}
		
		Symbol(int width, Segment[] segs)
		{
			this.width = width;
			this.segs  = segs;
		}
	}
	
	// Font data:
	/**
	 * The mapping between the characters of the text and the symbols to write.
	 */
	protected final Map<Character, Symbol> symbols = new HashMap<Character, Symbol>();

	/**
	 * Vertical size of the symbol grid.
	 */
	protected final int gridY;
	
	/**
	 * Aspect ratio of the symbol grid.
	 */
	protected final float aspectRatio;
	
	/**
	 * Size of the space between successive written symbols, in horizontal symbol grid spaces.
	 */
	protected final float kern;

	/**
	 * The minimum writing size at which the font is considered legible.
	 */
	protected final float minSize;
	
	/**
	 * The font's name.
	 */
	protected final String fontName;

	// Context while writing:
	float offset;
	float spaceY;
	float spaceX;

	// Curve stuff:
	final static int numSlices = 8;
	final static float[] cosi = new float[numSlices + 1];
	final static float[] sini = new float[numSlices + 1];

	static
	{
    	final double angStep = Math.PI / 2 / numSlices;

    	for (int i = 0; i <= numSlices; i++)
    	{
    		double ang = angStep * i;
    		cosi[i] = (float)Math.cos(ang);	
    		sini[i] = (float)Math.sin(ang);
    	}
	}

	/**
	 * Base Constructor.  Initialises the font's render parameters.
	 * 
	 * @param name The font's name.
	 * @param gridY The amount of rectangles on the symbol grid, vertically.
	 * @param aspectRatio The width of a symbol grid rectangle, in terms of its height.
	 * @param kern The separation between successively written symbols, in terms of the width of the symbol grid rectangles.
	 */
	protected VectorFont(String name, int gridY, float aspectRatio, float kern)
	{
		this.fontName = name;
		this.gridY = gridY;
		this.aspectRatio = aspectRatio;
		this.kern = kern;
		this.minSize = (aspectRatio >= 1) ? (gridY * 2 + 1) : ((gridY * 2 + 1) / aspectRatio);
	}
	
	/**
	 * Writes text to an OpenGL enabled canvas.

	 * @param gl The current OpenGL context.
	 * @param color The colour of the writing.
	 * @param text The text to write.
	 * @param posX The x-coordinate at the left, right or centre, depending on the alignment.
	 * @param posY The y-coordinate at the bottom.
	 * @param size The vertical size of the writing.
	 * @param align The text's alignment.  Can be <code>Font.ALIGN_LEFT</code>/<code>RIGHT</code>/<code>CENTER</code>.
	 * @param canvasWidth The width of the canvas, in pixels.
	 * @param canvasHeight The height of the canvas, in pixels.
	 */
	public void write(GL gl, String[] text, Color color, int posX, int posY, int size, byte align, int canvasWidth, int canvasHeight)
	{
		if (size < minSize)
		{
			logger.debug("Oversizing for legibility: Font = " + fontName + ", minSize = " + minSize + ", size = " + size);
			size = (int)Math.ceil(minSize);
		}

		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		gl.glTranslatef(-1, -1, 0);
		gl.glScalef(2f / canvasWidth, 2f / canvasHeight, 1);

		// Set up the context:
		offset = 0;
		spaceY = size / gridY;
		spaceX = spaceY * aspectRatio;
		
		if (spaceX < 2)
			spaceX = 2;

		if (spaceY < 2)
			spaceY = 2;
		
		// Draw symbols:
		for (char c : text[0].toCharArray())	// TODO Update to handle multi-line
		{
			// TODO Special characters, e.g. colour changers, escape chars etc.
			Symbol sym = symbols.get(c);
			
			if (sym == null)
			{
				logger.debug("Font '" + fontName + "' does not have a symbol for '" + c + "'");
				offset += spaceX;
				continue;
			}
/*
			for (Segment seg : sym.segs)
			{
				seg.drawOutline(gl);
			}
*/			
			for (Segment seg : sym.segs)
			{
				seg.drawFill(gl, color);
			}
			
			offset += (sym.width + kern) * spaceX;
		}
	}
}
